import time

import cv2
import numpy as np
import pyautogui
import win32api
import win32con
import win32gui
import win32ui
from PIL import Image

DEV_WIDTH = 1920
DEV_HEIGHT = 1080


class Screen:
    def __init__(self, hwnd,is_get_client_pos=True):
        # 初始化状态变量以存储前一次的宽度、高度和比率
        self.prev_width = None
        self.prev_height = None
        self.prev_rate = None

        self.hwnd = hwnd
        if is_get_client_pos:
            self.get_client_pos()  # 初始化大小，偏移量

    def is_current_window(self):
        """
        :return: 当前激活窗口的句柄。
        """
        return self.hwnd == win32gui.GetForegroundWindow()

    # 窗口改变大小，或者移动时，需要重新计算
    def get_client_pos(self):
        # print(win32gui.GetWindowRect(self.hwnd))
        # print(win32gui.GetClientRect(self.hwnd))
        # 获取窗口位置
        x1, y1, x2, y2 = win32gui.GetWindowRect(self.hwnd)
        # 标题  <Win32Window left="1138", top="169", width="1942", height="1136", title="原神">
        x3, y3, self.width, self.height = win32gui.GetClientRect(self.hwnd)  # 窗口大小
        left, top = win32gui.ClientToScreen(self.hwnd, (x3, y3))
        self.pos_left = left  # left()
        self.pos_top = top  # top

        right, down = win32gui.ClientToScreen(self.hwnd, (self.width, self.height))
        left -= x1
        top -= y1
        right -= x2
        down -= y2
        self.pos_x = left  # x偏移量
        self.pos_y = top  # y偏移量
        self.rate = 1.0 * self.width / DEV_WIDTH
        self.rate2 = 1.0 * self.height / DEV_HEIGHT

        # 检查宽度、高度和比率是否发生变化
        if self.width != self.prev_width or self.height != self.prev_height or self.rate != self.prev_rate:
            # 打印信息
            print('窗口宽高:', self.width, self.height, '加偏移后的left top:(图片左上角坐标)', self.pos_left,
                  self.pos_top,
                  '偏移量:', left, top, '与开发时的坐标比例:', self.rate)
            # 更新状态变量
            self.prev_width = self.width
            self.prev_height = self.height
            self.prev_rate = self.rate

        if self.rate != self.rate2:
            print('窗口宽高比例不是16:9')
            raise

        return left, top, right, down

    # 获取窗口宽,高
    def capture(self):
        x, y, x2, y2 = win32gui.GetWindowRect(self.hwnd)
        width = x2 - x
        height = y2 - y
        # 截图
        hWndDc = win32gui.GetWindowDC(self.hwnd)
        mfcDc = win32ui.CreateDCFromHandle(hWndDc)
        saveDc = mfcDc.CreateCompatibleDC()
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(mfcDc, width, height)
        saveDc.SelectObject(saveBitMap)
        saveDc.BitBlt((0, 0), (width, height), mfcDc, (0, 0), win32con.SRCCOPY)
        # bmpinfo = saveBitMap.GetInfo()
        # 保存图片转cv图像 将截图数据转换为NumPy数组
        bmpstr = saveBitMap.GetBitmapBits(True)
        im_open_cv = np.frombuffer(bmpstr, dtype=np.uint8)
        im_open_cv.shape = (height, width, 4)  # 注意这里的顺序应该是(height, width, 4)
        # 转换为BGR格式
        img = cv2.cvtColor(im_open_cv, cv2.COLOR_BGRA2BGR)
        # 偏移处理
        x1, y1, x2, y2 = self.get_client_pos()
        if x1 != 0 and y1 != 0:
            img = img[y1:y2, x1:x2]
        # 显示图像
        # cv2.imshow('Captured Image', img)
        # 释放内存
        win32gui.DeleteObject(saveBitMap.GetHandle())
        saveDc.DeleteDC()
        mfcDc.DeleteDC()
        win32gui.ReleaseDC(self.hwnd, hWndDc)
        return img

    # 获取窗口宽,高
    def capture_BGRA(self):
        x, y, x2, y2 = win32gui.GetWindowRect(self.hwnd)
        width = x2 - x
        height = y2 - y
        # 截图
        hWndDc = win32gui.GetWindowDC(self.hwnd)
        mfcDc = win32ui.CreateDCFromHandle(hWndDc)
        saveDc = mfcDc.CreateCompatibleDC()
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(mfcDc, width, height)
        saveDc.SelectObject(saveBitMap)
        saveDc.BitBlt((0, 0), (width, height), mfcDc, (0, 0), win32con.SRCCOPY)
        # bmpinfo = saveBitMap.GetInfo()
        # 保存图片转cv图像 将截图数据转换为NumPy数组
        bmpstr = saveBitMap.GetBitmapBits(True)
        im_open_cv = np.frombuffer(bmpstr, dtype=np.uint8)
        im_open_cv.shape = (height, width, 4)  # 注意这里的顺序应该是(height, width, 4)

        # 将NumPy数组转换为PIL图像对象
        img = Image.fromarray(im_open_cv)

        # 保存图像为BGRA格式的PNG文件
        img.save("screenshot2.png", "PNG")
        # 缩放图像为原来的一半大小
        # im_pil_resized = im_pil.resize((width // 2, height // 2), Image.ANTIALIAS)
        # 转换为BGR格式
        # img = cv2.cvtColor(im_open_cv, cv2.COLOR_BGRA2BGR)
        # 偏移处理
        x1, y1, x2, y2 = self.get_client_pos()
        if x1 != 0 and y1 != 0:
            # 裁剪图像
            img = img.crop((x1, y1, x1 + self.width, y1 + self.height))
            # 保存裁剪后的图像
            img.save("screenshot_croped.png", "PNG")
        # if x1 != 0 and y1 != 0:
        #     img = img[y1:y2, x1:x2]
        # 显示图像
        cv2.imshow('Captured Image', img)
        # 释放内存
        win32gui.DeleteObject(saveBitMap.GetHandle())
        saveDc.DeleteDC()
        mfcDc.DeleteDC()
        win32gui.ReleaseDC(self.hwnd, hWndDc)
        return img

    # 截取图像范围
    @staticmethod
    def cut_img(img, x1, y1, x2, y2):
        if None in [x1, y1, x2, y2] or sum([x1, y1, x2, y2]) == 0:
            return img
        height, width = img.shape[:2]
        if y1 <= y2 <= height and x1 <= x2 <= width:
            return img[y1:y2, x1:x2]  # , x1, y1, x2, y2
        else:
            raise "x1,y1,x2,y2图像范围溢出"

    # 开发时的x坐标转换为,当前窗口大小的坐标
    def dev_x(self, x):
        # return self.left + self.pos_x + x, self.top + self.pos_y + y
        return int(self.rate * x)

    def dev_y(self, y):
        # return self.left + self.pos_x + x, self.top + self.pos_y + y
        return int(self.rate * y)

    # 转换为全屏的绝对坐标
    def reality_point(self, x, y):
        # return self.left + self.pos_x + x, self.top + self.pos_y + y
        return self.pos_left + x, self.pos_top + y

    def moveTo(self, x, y, duration=0.0):
        x, y = self.reality_point(x, y)
        pyautogui.moveTo(x, y, duration=duration)

    # 原地停留1秒，在相对移动
    def moveRel(self, x, y):
        x, y = self.reality_point(x, y)
        pyautogui.moveRel(x, y, 1)  # 绝对移动

    # 原地停留1秒，在相对移动
    def dragTo(self, x, y, duration=0.0, button='left'):
        x, y = self.reality_point(x, y)
        pyautogui.dragTo(x, y, duration=duration, button=button)

    def click(self, x, y, clicks=1, interval=0.0, button='left', duration=0.0):
        x, y = self.reality_point(x, y)
        pyautogui.click(x, y, clicks, interval, button=button, duration=duration)  # 绝对移动

    def click_center(self, clicks=1, interval=0.0, button='left', duration=0.0):
        x, y = self.reality_point(self.width / 2, self.height / 2)
        pyautogui.click(x, y, clicks, interval, button=button, duration=duration)  # 绝对移动

    def turn_left(self, px: int = 500):
        win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, -px, 0, 0, 0)

    def turn_right(self, px:int=500):
        win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, px, 0, 0, 0)

    def turn_up(self, px: int = 500):
        win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, 0,-px,  0, 0)

    def turn_down(self, px:int=500):
        win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, 0, px, 0, 0)


# pyautogui.moveRel(500, 500, 1)  # 原地停留1秒，在相对移动
# pyautogui.dragTo(50, 50, 1, button='left')  # 按住鼠标左键，把鼠标拖拽到(50, 50)位置
# pyautogui.click(100, 100, 2, 0.25, button='left')  # 相对拖拽,按住鼠标左键，用0.25秒钟把鼠标向上拖拽
# ### 6、鼠标点击
# pyautogui.click(10, 20, 2, 0.25, button='left')  # 在坐标10，20处，点击左键2次，每次间隔0.25,button属性可以设置成left，middle和right。

if __name__ == '__main__':
    hwnd = win32gui.FindWindow(None, '原神')
    print('窗口句柄:', hwnd)
    if not hwnd:
        print('找不到窗口')
        exit(0)
    sc = Screen(hwnd)
    time.sleep(1)
    sc.turn_left()
    sc.turn_left(int(sc.width / 2))
    time.sleep(1)
    sc.turn_left()
    # sc.turn_left(int(sc.width / 2))
